home *** CD-ROM | disk | FTP | other *** search
- /*(( "Header" */
- /*
- * $Id: httpproxy.c,v 0.12 1996/02/12 19:23:36 mshopf Exp mshopf $
- *
- * (c) 1995 Matthias Hopf
- *
- * A small little Http Proxy.
- * It can serv as a ProxyProxy, too (i.e. it can perform only caching and will
- * get its data from another proxy).
- * That way it can be used for other protocol types than html, too.
- *
- * Run it standalone at high priority. It won't need much computing time as
- * it does no busy wait at all.
- * If you really want to re-get an already cached page, just reload it immedeately.
- * (no other request inbetween and no more than ReloadTime seconds delay).
- * If you browse offline, you'll get a note that the cache is invalid. Reload the
- * page if you want to queue the page and get the old cache.
- */
-
- /*
- * $Log: httpproxy.c,v $
- * Revision 0.12 1996/02/12 19:23:36 mshopf
- * assert() to logfile and debugfile and stderr.
- * MaxRequests now variable.
- * ReadCacheList()/SaveCacheList().
- * lots of bug fixes and cosmetic changes.
- *
- * Revision 0.11 1996/01/15 22:27:58 mshopf
- * fixed AmiTCP4.0 broken time() / stat() times (fix by Fionn Behrens).
- * another couple of bug fixes.
- * added dynamic cache table allocation.
- * error replies still getting better.
- * removed host realname lookup.
- *
- * Revision 0.10 1996/01/09 17:20:35 mshopf
- * Three major and some other minor bug fixes.
- * Added UrlBuffer for POST method and non-proxyproxy support.
- * More and more comformant error messages.
- * basic POST support (only proxying, no caching, no queuing).
- * Sending data even when request is not completed yet.
- * Recreating URL even from nonconformant requests.
- * Long requests supported now.
- * Some cosmetic changes.
- *
- * Revision 0.9 1995/12/06 19:53:55 mshopf
- * added fixes for unix machines and AmiTCP4.0 compilation.
- * some bug fixes.
- * more html conform now.
- * lots of printf type fixes.
- *
- * Revision 0.8 1995/12/03 14:20:23 mshopf
- * Made auto requests and proxy messages more http conform.
- *
- * Revision 0.7 1995/11/19 17:57:50 mshopf
- * added revbump compatible version string.
- * fixed ftp offline proxyproxy std port bug.
- *
- * Revision 0.6 1995/11/04 11:26:13 mshopf
- * better shutdown (requeueing of current transmissions). small bug fixes.
- *
- * Revision 0.5 1995/11/02 18:26:13 mshopf
- * queueing system implemented.
- *
- * Revision 0.4 1995/10/21 21:28:37 mshopf
- * small bug fix.
- *
- * Revision 0.3 1995/10/17 19:23:09 mshopf
- * cleaned up messy logging system.
- * new option 'log'.
- *
- * Revision 0.2 1995/10/13 18:09:17 mshopf
- * everything works so far, exceptions are noted in the header.
- * :-)
- *
- * Revision 0.1 1995/10/13 10:44:27 mshopf
- * no caching at all right now, but it works fine as a proxyproxy.
- *
- */
-
-
- /*)) */
- /*(( "Includes" */
-
- #include "httpproxy_rev.h"
- #include <stdio.h>
- #include <stdlib.h>
- #include <stddef.h>
- #include <errno.h>
- #include <string.h>
- #include <unistd.h>
- #include <time.h>
- #include <ctype.h>
-
- #include <syslog.h>
- #include <dirent.h>
- #include <sys/stat.h>
- #include <sys/syslog.h>
- #include <sys/types.h>
- #include <sys/time.h>
- #include <sys/ioctl.h>
- #include <sys/socket.h>
- #include <netdb.h>
- #include <netinet/in.h>
- #include <arpa/inet.h>
-
- #ifdef _AMIGA
- # include <bsdsocket.h>
- # define ioctl IoctlSocket
- # define ioctl_t long
- # define strcasecmp stricmp
- # ifdef sys_errlist
- # define strerror(x) (sys_errlist [x]) /* Won't necessary for AmiTCP4.0 */
- # endif
- # define CURRENTDIR ""
- typedef long len_t;
-
- # ifdef FIXTIME
- /* Fix broken AmiTCP4.0 (or SasC+AmiTCP4.0) time() / stat() */
- # include <proto/dos.h>
-
- time_t time (time_t *TimePtr)
- {
- struct DateStamp ds;
- time_t t;
- DateStamp (&ds);
-
- /* Sekunden + Minuten*60 +
- * (Tage + offset zu Unix time()[2922 Tage]) * 60 * 60 * 24 */
- t = ds.ds_Tick / 50+ds.ds_Minute*60+(ds.ds_Days+2922)*86400;
- if (TimePtr)
- *TimePtr = t;
- return (t);
- }
- # endif
-
- #else
- # define ioctl_t int
- # define CloseSocket close
- # define CURRENTDIR "."
- typedef int len_t;
- #endif
-
- #ifdef DEBUG
- # define debug(x) printf x
- #else
- # define debug(x) ((void) 0)
- #endif
-
- /* own assert() writing assertation to logfile */
- #ifndef NDEBUG
- # define assert(x) ASSERT ((int)(x), #x, __FILE__, __LINE__)
- void ASSERT (int x, const char *text, const char *file, int line)
- {
- extern FILE *LogStream;
- if (x)
- return;
- if (! text)
- text = "unknown";
- fprintf (stderr, "assertion (%s) failed in '%s' on line %d\n", text, file, line);
- debug (( "assertion (%s) failed in '%s' on line %d\n", text, file, line));
- if (LogStream)
- {
- fprintf (LogStream, "assertion (%s) failed in '%s' on line %d\n", text, file, line);
- fclose (LogStream);
- LogStream = NULL;
- }
- exit (20);
- /*NOTREACHED*/
- }
- #else
- # define assert(x) ((void) 0)
- #endif
-
- /*)) */
- /*(( "Types / Variables / Constants" */
-
- /* Types and constants */
-
- #define NAME_CACHETABLE ".cachetable"
- #define DEFAULT_PROXYPORT 8080
- #define DEFAULT_HTTPPORT 80
- #define DEFAULT_DELTIME (48*60*60) /* default: delete cached files on startup */
- #define DEFAULT_EXPIRETIME (24*60*60) /* default: delete cache, when the page is requested and too old */
- #define DEFAULT_RELOADTIME 10 /* default: expire cache on reload inbetween */
- #define DEFAULT_MAX_REQUESTS 8 /* Maximum number of pending requests */
-
- #define MIN_REQUESTS 4 /* Minimum number of free requests for interactive actions */
- #define MAX_CACHES 32 /* maximum number of cache slots per cachearray */
- #define MAX_FILENAME 20 /* maximum size of filename (>= 20) */
- #define MAX_URLSAVE 128 /* maximum size of saved url requests */
- #define MAX_URLBUFFER 512 /* no Url of any request may be larger than this value (<=MAX_REQBUFFER) */
- #define MAX_REQBUFFER 1024
- #define MAX_DATABUFFER 2048
- #define SHIFT_DATABUFFER 512 /* when x bytes are in the data buffer, shift it after send() */
- #define SHIFT_REQBUFFER 512 /* dto. for the request buffer */
-
- #define REQ_REQSOCKET 0x01 /* a request socket is open */
- #define REQ_CONNSOCKET 0x02 /* a connection socket is open / to be connected */
- #define REQ_DONE 0x04 /* the connection socket is already closed */
- #define REQ_REQDONE 0x08 /* the URL is completely read (cache may be sent) */
- #define REQ_HTTP1X0 0x10 /* the URL was sent with HTTP/1.0 */
- #define REQ_URLDONE 0x40 /* the URL request (the first) line is already there */
-
- #define CACHE_VALID 0x01 /* Cache is filled and ready to serve */
- #define CACHE_QUEUED 0x02 /* Cache is already queued for regetting */
- #define CACHE_DELETETMP 0x04 /* There's a temporaray cache file to be removed */
-
- #ifndef FALSE
- #define FALSE (0)
- #define TRUE (1)
- #endif
- /* Needed Memory */
- #define NEED_MEM ((MaxRequests * sizeof (request_t) + sizeof (cachearray_t)) / 1024)
-
- #define difftime(x,y) (((u_long)(x)) - ((u_long)(y)))
- #define isvalidhttp(x) ((unsigned char)(x) > ' ' && (x) != '%' && (x) != '\\' && (unsigned char)(x) < 128)
-
- /*
- * Some typical Flag combinations in typical order:
- * None: Empty slot
- * REQ_REQSOCKET: Awaiting URL
- * REQ_REQSOCKET | REQ_CONNSOCKET: Still geting URL, but 1. line is already there
- * *or* URL done
- * Awaiting connection to remote host
- * *or* already transfering data
- * REQ_REQSOCKET | REQ_DONE: The connection socket is already closed, but data is
- * still to be delivered, or data is sent from the cache
- * REQ_CONNSOCKET: Request was terminated, still geting data to fill up
- * the cache (TIMEOUT needed!!!)
- */
-
- /* REQ_REQSOCKET: alone will never occour on ProxyProxy==TRUE... */
-
-
- typedef struct {
- int Flags;
- char File [MAX_FILENAME]; /* "" if free slot / with begining '_' (data) */
- char Url [MAX_URLSAVE]; /* "" if free slot or just getting data */
- } cache_t;
-
- typedef struct cachearray {
- struct cachearray *Next;
- cache_t Caches [MAX_CACHES];
- } cachearray_t;
-
- typedef struct {
- long ReqSocket, ConnSocket;
- int Flags;
- char ReqBuffer [MAX_REQBUFFER];
- char UrlBuffer [MAX_URLBUFFER];/* This one contains the URL line for sending the request */
- char DataBuffer [MAX_DATABUFFER];
- int ReqSent, ReqRecv;
- int UrlSent, UrlRecv;
- int DataSent, DataRecv;
- cache_t *Cache; /* NULL if not cacheable */
- FILE *Stream; /* != NULL on open data transfer to/from cache */
- } request_t;
-
-
- /* Global variables */
-
- char *VVersion= VERSTAG;
- char *Version = VERSTRING;
- FILE *LogStream = NULL;
- char *PrgName;
- long ServerSocket;
- int StdHttpPort;
- int ServerPort = DEFAULT_PROXYPORT;
- int ProxyProxy = FALSE; /* TRUE, when all requests should be
- * forwarded to another proxy. */
- struct sockaddr_in ProxyProxyIn;
-
- cache_t *LastCache = NULL;
- time_t ThisRequestTime = 0;
- time_t StartUpTime;
- int MaxRequests = DEFAULT_MAX_REQUESTS;
- int RequestsFree = -1;
- int CachesFree = MAX_CACHES;
- long CacheNr = 0;
- u_long DelCacheTime = DEFAULT_DELTIME;
- u_long ExpireCacheTime = DEFAULT_EXPIRETIME;
- u_long ReloadCacheTime = DEFAULT_RELOADTIME;
- int OffLine = 0; /* 0: get cache files again, when they are too old */
- /* 1: keep cache files and queue them */
- int GetQueued = 0; /* 1: get queued data from remote hosts or proxyproxy */
- int CacheUnreadRequests = FALSE; /* 1: keep cache data on data connection close with data in url send buffer */
- request_t *Requests;
- cachearray_t *Caches;
-
-
- void DeleteConnect (request_t *Req, int ok);
- void SaveCacheUrl (cache_t *c);
-
- /*)) */
-
- /*(( "Init ()" */
-
- /* Init all global variables, open server port */
-
- void Init (char *ProxyProxyHost, int ProxyProxyPort, char *LogName)
- {
- struct servent *ServEnt;
- struct sockaddr_in SockIn;
- struct hostent *HostEnt;
- ioctl_t on = 1;
-
- /* Standard setups */
-
- if (! (LogStream = fopen (LogName, "a+")) )
- {
- fprintf (stderr, "cannot open logfile '%s': %s\n", LogName, strerror (errno));
- exit (20);
- }
-
- if (! (Requests = calloc (MaxRequests, sizeof (request_t))) )
- {
- fprintf (stderr, "not enough memory (need %d Kbyte)\n", NEED_MEM);
- exit (20);
- }
- RequestsFree = MaxRequests;
-
- if (! (Caches = calloc (1, sizeof (cachearray_t))) )
- {
- fprintf (stderr, "not enough memory (need %d Kbyte)\n", NEED_MEM);
- exit (20);
- }
- assert (Caches->Next == NULL);
-
- ThisRequestTime = StartUpTime = time (NULL);
-
- /* Get standard HTTP port (80 at the time of development...but you never know) */
-
- if (! (ServEnt = getservbyname ("http", "tcp")) )
- {
- StdHttpPort = DEFAULT_HTTPPORT;
- syslog (LOG_WARNING, "%s: unknown protocol 'http', using default port %d", PrgName, DEFAULT_HTTPPORT);
- }
- else
- StdHttpPort = ServEnt->s_port;
-
- /* Create Serversocket */
-
- if ( (ServerSocket = socket (PF_INET, SOCK_STREAM, 0)) < 0)
- {
- fprintf (stderr, "socket() for serverport failed: %s\n", strerror (errno));
- exit (20);
- }
-
- memset ((char *) &SockIn, 0, sizeof (struct sockaddr_in));
- SockIn.sin_family = AF_INET;
- SockIn.sin_addr.s_addr = INADDR_ANY;
- SockIn.sin_port = htons (ServerPort);
- if (bind (ServerSocket, (struct sockaddr *) &SockIn, sizeof (struct sockaddr_in)) < 0)
- {
- fprintf (stderr, "bind() failed for port %d: %s\n", ServerPort, strerror (errno));
- exit (20);
- }
-
- #ifdef FIOASYNC
- if (ioctl (ServerSocket, FIOASYNC, (caddr_t) &on) < 0)
- {
- fprintf (stderr, "ioctl() failed for FIOASYNC: %s\n", strerror (errno));
- exit (20);
- }
- #endif
- #ifdef FIONBIO
- if (ioctl (ServerSocket, FIONBIO, (caddr_t) &on) < 0)
- {
- fprintf (stderr, "ioctl() failed for FIONBIO: %s\n", strerror (errno));
- exit (20);
- }
- #endif
-
- if (listen (ServerSocket, 4) < 0)
- {
- fprintf (stderr, "listen() failed: %s\n", strerror (errno));
- exit (20);
- }
-
- /* Get ProxyProxy address */
-
- if (ProxyProxyHost)
- {
- ProxyProxy = TRUE;
-
- if (! OffLine)
- {
- memset ((char *) &ProxyProxyIn, 0, sizeof (struct sockaddr_in));
- ProxyProxyIn.sin_family = AF_INET;
- ProxyProxyIn.sin_port = htons (ProxyProxyPort);
-
- if ( (ProxyProxyIn.sin_addr.s_addr = inet_addr (ProxyProxyHost)) +0 == -1) /* !!! */
- {
- if (! (HostEnt = gethostbyname (ProxyProxyHost)) )
- {
- fprintf (stderr, "can't get proxyproxy host '%s'\n", ProxyProxyHost);
- exit (20);
- }
- else
- memmove (&ProxyProxyIn.sin_addr, HostEnt->h_addr, sizeof (struct in_addr));
- }
- fprintf (LogStream, "%s\nstarting with proxyproxy host '%s', port %d\n",
- Version, ProxyProxyHost, ProxyProxyPort);
- debug (("%s\nstarting with proxyproxy host '%s', port %d\n",
- Version, ProxyProxyHost, ProxyProxyPort));
- return;
- }
- }
- else if (! OffLine)
- {
- fprintf (LogStream, "%s\nstarting in normal mode\n", Version);
- debug (("%s\nstarting in normal mode\n", Version));
- return;
- }
-
- fprintf (LogStream, "%s\nstarting in offline mode\n", Version);
- debug (("%s\nstarting in offline mode\n", Version));
- }
-
-
- /*)) */
- /*(( "InitCacheSlot()/GetFreeCacheSlot()/ErrToReq()" */
-
- /* Initialize cache slot */
-
- void InitCacheSlot (cache_t *c, cache_t *Template, char Init, char Separator)
- {
- if (! Template)
- {
- c->Flags = 0;
- c->Url[0] = '\0';
- sprintf (c->File, "%c%08lx%c%08lx", Init, StartUpTime, Separator, CacheNr++);
- debug (("new cache allocated: '%s'\n", c->File));
- }
- else
- {
- c->Flags = Template->Flags & ~CACHE_VALID;
- strcpy (c->Url, Template->Url);
- sprintf (c->File, "%c%8.8s%c%8.8s", Init, & Template->File [1], Separator, & Template->File [10]);
- debug (("cache %s from template %s\n", c->File, Template->File));
- }
- }
-
-
- /* Scan for a free cache slot */
-
- cache_t *GetFreeCacheSlot (void)
- {
- cache_t *c;
- cachearray_t *a = Caches;
- int i;
-
- debug (("new cache requested.\n"));
- if (! CachesFree)
- {
- while (a->Next)
- a = a->Next;
-
- if (! (a->Next = calloc (1, sizeof (cachearray_t))) )
- {
- syslog (LOG_NOTICE, "%s: no more memory for cache arrays", PrgName);
- return (NULL);
- }
- assert (a->Next->Next == NULL);
- debug (("allocated %d new cache slots at 0x%x\n", MAX_CACHES, a->Next));
- a = a->Next;
- CachesFree += MAX_CACHES - 1;
- c = & a->Caches [0];
- InitCacheSlot (c, NULL, '_', '.');
- return (& a->Caches [0]);
- }
-
- while (a)
- {
- for (c = a->Caches, i=0; i < MAX_CACHES; c++, i++)
- if (c->Url[0] == '\0' && c->File[0] == '\0')
- {
- CachesFree--;
- InitCacheSlot (c, NULL, '_', '.');
- return (c);
- }
- a = a->Next;
- }
- assert (0); /*NOTREACHED*/
- }
-
-
- /* Type an error to a data buffer of a pending request and set everything up.
- * The error is printed into the Logstream, too. When Short == NULL, a information
- * message is sent, and nothing is printed into the Logstream. */
-
- void ErrToReq (request_t *Req, int Number, char *Short, char *Descr)
- {
- static char *Author = "<A HREF=\"http://wwwcip.informatik.uni-erlangen.de/user/mshopf\">Matthias Hopf</A>";
- Req->DataSent = 0;
-
- if (Req->Flags & REQ_HTTP1X0)
- {
- if (Short)
- {
- if (errno > 0)
- {
- fprintf (LogStream, "#%02d: Error: %s: %s\n", Req-Requests, Short, strerror (errno));
- debug (("#%02d: Error: %s: %s\n", Req-Requests, Short, strerror (errno)));
- }
- else
- {
- fprintf (LogStream, "#%02d: Error: %s\n", Req-Requests, Short);
- debug (("#%02d: Error: %s\n", Req-Requests, Short));
- }
-
- sprintf (Req->DataBuffer, "HTTP/1.0 %03d %s\r\n"
- "Server: %s\r\n"
- "Content-Type: text/html\r\n\r\n"
- "<HTML><HEAD><TITLE>Proxy Error</TITLE></HEAD>\n"
- "<BODY><H1>Proxy Error: %s</H1><P>\n"
- "%s%s%s"
- "%s<P>\n"
- "<HR><ADDRESS>%s by %s</ADDRESS>\n"
- "</BODY></HTML>\n",
- Number, Short, VERSHTTP, Short,
- (errno > 0 ? "<H3>Cause: " : ""),
- (errno > 0 ? strerror (errno) : ""),
- (errno > 0 ? "</H3>\n" : ""),
- Descr, Version, Author);
- }
- else
- sprintf (Req->DataBuffer, "HTTP/1.0 %03d Proxy Message\r\n"
- "Server: %s\r\n"
- "Content-Type: text/html\r\n\r\n"
- "<HTML><HEAD><TITLE>Proxy Message</TITLE></HEAD>\n"
- "<BODY><H3>Proxy Message:</H3><P>\n"
- "%s<P>\n"
- "<HR><ADDRESS>%s by %s</ADDRESS>\n"
- "</BODY></HTML>\n",
- Number, VERSHTTP, Descr, Version, Author);
- }
- else
- {
- if (Short)
- {
- fprintf (LogStream, "#%02d: Error for non-Http1.0 request: %s\n", Req-Requests, Short);
- debug (("#%02d: Error for non-Http1.0 request : %s\n", Req-Requests, Short));
-
- sprintf (Req->DataBuffer, "<HTML><HEAD><TITLE>Proxy Error</TITLE></HEAD>\n"
- "<BODY><H1>Proxy Error: %s</H1><P>\n"
- "%s%s%s"
- "%s<P>\n"
- "<HR><ADDRESS>%s by %s</ADDRESS>\n"
- "</BODY></HTML>\n",
- Short,
- (errno > 0 ? "<H3>Cause: " : ""),
- (errno > 0 ? strerror (errno) : ""),
- (errno > 0 ? "</H3>\n" : ""),
- Descr, Version, Author);
- }
- else
- sprintf (Req->DataBuffer, "<HTML><HEAD><TITLE>Proxy Message</TITLE></HEAD>\n"
- "<BODY><H3>Proxy Message:</H3><P>\n"
- "%s<P>\n"
- "<HR><ADDRESS>%s by %s</ADDRESS>\n"
- "</BODY></HTML>\n",
- Descr, Version, Author);
- }
-
- Req->DataRecv = strlen (Req->DataBuffer);
- Req->Flags |= REQ_DONE;
- if (Req->Flags & REQ_CONNSOCKET)
- DeleteConnect (Req, FALSE);
- }
-
-
- /*)) */
- /*(( "RemCacheEntry()/CheckCacheTime()" */
-
- /* Remove a cache entry and its according files */
-
- void RemCacheEntry (cache_t *c)
- {
- cache_t tmp;
-
- debug (("deleting cache file '%s', additional url: %s\n", c->File, c->Url[0] ? "yes" : "no"));
- if (c->File[0] && c->File[0] != '.') /* It's no special message file */
- {
- if (remove (c->File))
- {
- fprintf (LogStream, "cannot delete invalid cache file '%s': %s\n", c->File, strerror (errno));
- debug (("cannot delete invalid cache file '%s': %s\n", c->File, strerror (errno)));
- }
- if (c->Url[0])
- {
- c->File[0] = '@';
- if (remove (c->File))
- {
- fprintf (LogStream, "cannot delete invalid cache file '%s': %s\n", c->File, strerror (errno));
- debug (("cannot delete invalid cache file '%s': %s\n", c->File, strerror (errno)));
- }
- }
- if (c->Flags & CACHE_DELETETMP)
- {
- InitCacheSlot (&tmp, c, '@', '@');
- debug (("removing temporary url file '%s'\n", tmp.File));
- if (remove (tmp.File))
- {
- fprintf (LogStream, "error while removing temporary url file '%s': %s\n", tmp.File, strerror (errno));
- debug (("error while removing temporary url file '%s': %s\n", tmp.File, strerror (errno)));
- }
- }
- }
-
- c->File[0] = c->Url[0] = '\0';
- c->Flags = 0;
- CachesFree++;
- }
-
-
- /* Check the modification time and date of a cache entry; set Time to 0 to force expire. */
- /* Returns -1, when the cache entry is expired or to be reloaded or not existing, the entry is removed.
- * Returns -2, when the cache entry is expired and queued. */
-
- int CheckCacheTime (cache_t *c, u_long Time)
- {
- time_t FileT = -1;
- struct stat s;
-
- if (stat (c->File, &s) >= 0)
- {
- FileT = s.st_mtime;
- debug (("local 0x%lx, access 0x%lx, diff1 %ld <?> %s\n", ThisRequestTime, FileT,
- difftime (ThisRequestTime, FileT), difftime (ThisRequestTime, FileT) > Time ? "expired" : "valid"));
- if ((! Time) || difftime (ThisRequestTime, FileT) > Time)
- {
- if (OffLine)
- {
- if ((c->Flags & CACHE_VALID) && ! (c->Flags & CACHE_QUEUED))
- { /* When CACHE_VALID is not set, the routine was called from buildcache() or similar */
- cache_t tmp;
- c->Flags |= CACHE_QUEUED;
- InitCacheSlot (&tmp, c, '@', '@'); /* Queue Url for cache entry and return -2 */
- SaveCacheUrl (&tmp);
- }
- return (-2);
- }
- else if (c->Flags & CACHE_QUEUED) /* Is the entry queued and not yet sent? */
- return (OffLine ? -2 : -1);
- else
- FileT = -1;
- }
- }
-
- if (FileT == -1)
- {
- if (c->Flags & CACHE_QUEUED)
- {
- debug (("cache entry '%s' not yet there (queued) - This should not happen...\n", c->File));
- fprintf (stderr, "%s: Warning! Data consistency failure, line %d\n", PrgName, __LINE__);
- return (-2);
- }
- else
- {
- debug (("cache entry '%s' expired / to be reloaded, removing\n", c->File));
- RemCacheEntry (c);
- return (-1);
- }
- }
- return (c->Flags & CACHE_QUEUED ? -2 : 0);
- }
-
- /*)) */
- /*(( "ReadCacheUrl()/SaveCacheUrl()" */
-
- /* Read the Url from a specific url file */
-
- void ReadCacheUrl (cache_t *c, char *Name)
- {
- FILE *f;
-
- c->Url[0] = ' '; /* RemCacheEntry shall remove the url file, too, in case it is called */
- switch (CheckCacheTime (c, DelCacheTime)) {
- case 0:
- case -3:
- break;
-
- case -1:
- return;
- default:
- RemCacheEntry (c);
- return;
- }
-
- if (! (f = fopen (Name, "r")) )
- {
- fprintf (LogStream, "cannot open url file '%s', removing cache entry: %s\n", Name, strerror (errno));
- debug (("cannot open url file '%s', removing cache entry: %s\n", Name, strerror (errno)));
- RemCacheEntry (c);
- return;
- }
- if (fgets (c->Url, MAX_URLSAVE, f))
- {
- switch (c->Url [strlen (c->Url) - 1]) {
- case '\n':
- case '\r':
- break;
-
- default: /* Cache entry is valid -> return */
- fclose (f);
- c->Flags = CACHE_VALID;
- debug (("valid cache entry: url '%s', data '%s'\n", c->Url, c->File));
- return;
- }
- }
- fclose (f);
- fprintf (LogStream, "corrupt cache entry for url file '%s', removing\n", Name);
- debug (("corrupt cache entry for url file '%s', removing\n", Name));
- RemCacheEntry (c);
- }
-
-
- /* Save the cache's Url to its url file */
-
- void SaveCacheUrl (cache_t *c)
- {
- FILE *UrlStream;
- cache_t tmp;
-
- c->File[0] = '@';
- debug (("writing url file '%s'\n", c->File));
- if ( (UrlStream = fopen (c->File, "w")) )
- {
- fprintf (UrlStream, "%s", c->Url); /* no error checking - well... */
- fclose (UrlStream);
- c->File[0] = '_';
- if (! (c->Flags & CACHE_QUEUED))
- c->Flags |= CACHE_VALID;
- /* fprintf (LogStream, "URL '%s' is now cached\n",
- * c->Url);*/
- if (c->Flags & CACHE_DELETETMP)
- {
- InitCacheSlot (&tmp, c, '@', '@');
- debug (("removing temporary url file '%s'\n", tmp.File));
- if (remove (tmp.File))
- {
- fprintf (LogStream, "error while removing temporary url file '%s': %s\n", tmp.File, strerror (errno));
- debug (("error while removing temporary url file '%s': %s\n", tmp.File, strerror (errno)));
- }
- c->Flags &= ~CACHE_DELETETMP;
- }
- }
- else
- {
- fprintf (LogStream, "cannot write cache url file '%s': %s\n",
- c->File, strerror (errno));
- debug (("cannot write cache url file '%s': %s\n",
- c->File, strerror (errno)));
- c->File[0] = '_';
- if (c->Flags & CACHE_QUEUED)
- c->File[0] = '\0';
- RemCacheEntry (c);
- }
- }
-
-
- /*)) */
- /*(( "ReadCacheList()/SaveCacheList()" */
-
- /* Read the cache list file when it's there */
- /* Format: Fields (max size):
- * Filename (MAX_FILENAME) " " Flags (1) Url (MAX_URLSAVE)
- * Terminated with \n. */
- /* Flags: v:Valid q:Queued r:Requeued (expired cache entry exists) */
- /* <- -1: Failure 0: ok */
-
- int ReadCacheList (void)
- {
- cache_t *c;
- char Buffer [MAX_URLSAVE+MAX_FILENAME+3];
- FILE *Fd;
- char *p, *End;
-
- if (! (Fd = fopen (NAME_CACHETABLE, "r")))
- return -1;
-
- debug (("Cachetable valid.\n"));
- while (fgets (Buffer, MAX_URLSAVE+MAX_FILENAME+3, Fd))
- {
- End = Buffer + strlen (Buffer) - 1;
- *End = '\0'; /* Remove terminating '\n' */
- p = strchr (Buffer, ' ');
- if (p == NULL || p >= & Buffer [MAX_FILENAME])
- {
- fprintf (LogStream, "Invalid line in cachetable, skipping: '%s'\n", Buffer);
- debug (("Invalid line in cachetable, skipping: '%s'\n", Buffer));
- continue;
- }
- p[0] = '\0';
- if (p[1] != 'v' && p[1] != 'q' && p[1] != 'r')
- {
- fprintf (LogStream, "Unknown cache typ in cachetable, skipping: '%s'\n", Buffer);
- debug (("Unknown cache typ in cachetable, skipping: '%s'\n", Buffer));
- continue;
- }
- if (p[2] == '\0')
- {
- fprintf (LogStream, "Invalid line in cachetable, skipping: '%s'\n", Buffer);
- debug (("Invalid line in cachetable, skipping: '%s'\n", Buffer));
- continue;
- }
- assert (&p[2] < End);
- assert (strlen (&p[2]) < MAX_URLSAVE);
-
- if (! (c = GetFreeCacheSlot ()))
- break;
-
- strcpy (c->File, Buffer);
- strcpy (c->Url, &p[2]);
- if (p[1] == 'v')
- c->Flags = CACHE_VALID;
- else if (p[1] == 'q')
- c->Flags = CACHE_QUEUED;
- else
- c->Flags = CACHE_VALID | CACHE_QUEUED | CACHE_DELETETMP;
- debug (("read '%c' line from cachetable, file '%s', url '%s'\n", p[1], c->File, c->Url));
- }
-
- fclose (Fd);
- remove (NAME_CACHETABLE);
- return 0;
- }
-
-
- /* Save all cache entries in the cachetable file */
-
- void SaveCacheList (void)
- {
- cachearray_t *a;
- cache_t *c;
- int i;
- FILE *Fd;
-
- if (! (Fd = fopen (NAME_CACHETABLE, "w")))
- return;
-
- for (a = Caches; a; a = a->Next)
- for (i = 0, c = a->Caches; i < MAX_CACHES; i++, c++)
- if (c->File[0] && c->Url[0])
- {
- if ((c->Flags & CACHE_VALID) && (c->Flags & CACHE_QUEUED))
- {
- if (c->File[0] == '_')
- fprintf (Fd, "%s r%s\n", c->File, c->Url);
- else
- {
- fprintf (LogStream, "Warning! Requeued cache consistency error, file '%s', url '%s'\n", c->File, c->Url);
- fprintf (stderr, "Warning! Requeued cache consistency error, file '%s', url '%s'\n", c->File, c->Url);
- debug (("Warning! Requeued cache consistency error, file '%s', url '%s'\n", c->File, c->Url));
- }
- }
- else if (c->Flags & CACHE_VALID)
- {
- if (c->File[0] == '_')
- fprintf (Fd, "%s v%s\n", c->File, c->Url);
- else
- {
- fprintf (LogStream, "Warning! Valid cache consistency error, file '%s', url '%s'\n", c->File, c->Url);
- fprintf (stderr, "Warning! Valid cache consistency error, file '%s', url '%s'\n", c->File, c->Url);
- debug (("Warning! Valid cache consistency error, file '%s', url '%s'\n", c->File, c->Url));
- }
- }
- else if (c->Flags & CACHE_QUEUED)
- {
- if (c->File[0] == '@')
- fprintf (Fd, "%s q%s\n", c->File, c->Url);
- else
- {
- fprintf (LogStream, "Warning! Queued cache consistency error, file '%s', url '%s'\n", c->File, c->Url);
- fprintf (stderr, "Warning! Queued cache consistency error, file '%s', url '%s'\n", c->File, c->Url);
- debug (("Warning! Queued cache consistency error, file '%s', url '%s'\n", c->File, c->Url));
- }
- }
- else
- {
- fprintf (LogStream, "Warning! Unknown cache consistency error, file '%s', url '%s'\n", c->File, c->Url);
- fprintf (stderr, "Warning! Unknown cache consistency error, file '%s', url '%s'\n", c->File, c->Url);
- debug (("Warning! Unknown cache consistency error, file '%s', url '%s'\n", c->File, c->Url));
- }
- }
-
- fclose (Fd);
- }
-
- /*)) */
- /*(( "BuildCache ()" */
-
- /* Scan cache directory, build up cache and delete invalid cache entries */
-
- void BuildCache (void)
- {
- DIR *Dir;
- cachearray_t *aa, *aaa;
- cache_t *c, *cc, *ccc;
- struct dirent *d;
-
- if (! (Dir = opendir (CURRENTDIR)))
- {
- fprintf (stderr, "%s: cannot open cache directory: %s\n", PrgName, strerror (errno));
- exit (20);
- }
-
- errno = 0;
- c = GetFreeCacheSlot ();
- c->File [0] = '\0';
- assert (c != NULL);
-
- while ( (d = readdir (Dir)) )
- {
- if (strlen (d->d_name) > MAX_FILENAME-1)
- {
- if (d->d_name[0] != '.') /* no special file... */
- fprintf (stderr, "unknown file '%s' in cache directory\n", d->d_name);
- }
- else
- {
- strcpy (c->File, d->d_name);
- if (c->File [0] == '_') /* found cache data - look for cache url */
- {
- debug (("found data '%s'\n", c->File));
- aa = Caches;
- for (cc = aa->Caches; ; cc++)
- {
- if (cc >= & aa->Caches [MAX_CACHES])
- {
- aa = aa->Next;
- assert (aa != NULL);
- cc = aa->Caches;
- }
- if (cc == c)
- break;
- if (strcmp (&c->File[1], &cc->File[1]) == 0 && cc->File[0] == '@')
- {
- cc->File[0] = '_';
- c->File[0] = '@';
- ReadCacheUrl (cc, c->File); /* already read... */
- c->File[0] = '\0';
- break;
- }
- }
- if (c == cc) /* not found... */
- {
- if (! (c = GetFreeCacheSlot ()) )/* keep it, may be we'll get the url file, too */
- {
- fprintf (LogStream, "Cache table full while recacheing\n");
- debug (("Cache table full while recacheing\n"));
- break; /* break from while - no room left... */
- }
- c->File [0] = '\0';
- }
- }
- else if (c->File [0] == '@') /* found cache url - look for cache data */
- {
- debug (("found url '%s'\n", c->File));
- aa = Caches;
- for (cc = aa->Caches; ; cc++)
- {
- if (cc >= & aa->Caches [MAX_CACHES])
- {
- aa = aa->Next;
- assert (aa != NULL);
- cc = aa->Caches;
- }
- if (cc == c)
- break;
- if (strcmp (&c->File[1], &cc->File[1]) == 0 && cc->File[0] == '_')
- {
- ReadCacheUrl (cc, c->File); /* already read... */
- c->File[0] = '\0';
- break;
- }
- }
- if (c == cc) /* not found... */
- {
- if (! (c = GetFreeCacheSlot ())) /* keep it, may be we'll get the data file, too */
- {
- fprintf (LogStream, "Cache table full while recacheing\n");
- debug (("Cache table full while recacheing\n"));
- break; /* break from while - no room left... */
- }
- c->File [0] = '\0';
- }
- }
- else
- {
- if (c->File [0] != '.') /* .files, .httpproxy-log or standard directories (unix) */
- fprintf (stderr, "unknown file '%s' in cache directory\n", c->File);
- c->File[0] = '\0';
- }
- }
- }
- if (errno)
- fprintf (stderr, "directory error while building cache table, continueing: %s\n", strerror (errno));
- closedir (Dir);
- RemCacheEntry (c);
-
- /* now remove all incomplete cachentries (and files) */
- aa = Caches;
- for (cc = aa->Caches; ; cc++)
- {
- if (cc >= & aa->Caches [MAX_CACHES])
- {
- aa = aa->Next;
- assert (aa != NULL);
- cc = aa->Caches;
- }
- if (cc == c)
- break;
- if (cc->Url[0] == '\0' && cc->File [0] == '_')
- {
- fprintf (LogStream, "cache file '%s' invalid, removing.\n", cc->File);
- debug (("cache file '%s' invalid, removing.\n", cc->File));
- RemCacheEntry (cc);
- }
- }
-
- /* mark all url only caches as queued */
- aa = Caches;
- for (cc = aa->Caches; ; cc++)
- {
- if (cc >= & aa->Caches [MAX_CACHES])
- {
- aa = aa->Next;
- assert (aa != NULL);
- cc = aa->Caches;
- }
- if (cc == c)
- break;
- if (cc->File [0] == '@')
- {
- ReadCacheUrl (cc, cc->File); /* the file name remains '@...' */
- if (cc->File [0] == '@')
- {
- cc->Flags = CACHE_QUEUED; /* and ! CACHE_VAILD */
- aaa = Caches;
- for (ccc = aaa->Caches; ; ccc++)
- {
- if (ccc >= & aaa->Caches [MAX_CACHES])
- {
- aaa = aaa->Next;
- assert (aaa != NULL);
- ccc = aaa->Caches;
- }
- if (ccc == c)
- break;
- if (ccc->File [0] == '_')
- if (cc != ccc && strcmp (ccc->Url, cc->Url) == 0)
- {
- debug (("url '%s': queued url file %s associated with expired cache entry %s\n", cc->Url, cc->File, ccc->File));
- assert (ccc->Flags & CACHE_VALID);
- if (cc->File [9] != '@') /* it has to be a special queued url file... */
- {
- fprintf (LogStream, "Warning! Double cache consistency error, file '%s', url '%s', file2 '%s'\n", ccc->File, ccc->Url, cc->File);
- fprintf (stderr, "Warning! Double cache consistency error, file '%s', url '%s', file2 '%s'\n", ccc->File, ccc->Url, cc->File);
- debug (("Warning! Double cache consistency error, file '%s', url '%s', file2 '%s'\n", ccc->File, ccc->Url, cc->File));
- }
-
- ccc->Flags |= CACHE_QUEUED | CACHE_DELETETMP;
- cc->File[0] = '\0';
- RemCacheEntry (cc);
- ccc = c; /* break all */
- break;
- }
- }
- if (ccc == c)
- debug (("queued url '%s'\n", cc->Url));
- }
- }
- }
- }
-
-
- /*)) */
- /*(( "ScanCache()" */
-
- /* Scan whether a specific cache is already there, filled and ready to serve */
- /* Returns 0: no, not there 1: yes, all set up -1: Failure
- * 2: error message in buffer */
- /* Sets up the request struct for correct filling, opens all streams, etc. */
-
- int ScanCache (request_t *Req, char *Url, int Known)
- {
- int i;
- cache_t *c;
- cachearray_t *a;
- static time_t LastRequestTime;
-
- debug (("Searching for cache for Url '%s'\n", Url));
- LastRequestTime = ThisRequestTime;
- ThisRequestTime = time (NULL);
- debug (("Last: %ld, This: %ld, Diff: %ld, reload: %s\n", LastRequestTime, ThisRequestTime,
- difftime (ThisRequestTime, LastRequestTime),
- difftime (ThisRequestTime, LastRequestTime) < ReloadCacheTime ? "yes" : "no"));
-
- for (a = Caches; a; a = a->Next)
- for (c = a->Caches, i=0; i < MAX_CACHES; c++, i++)
- if (c->Url[0])
- {
- if (strcmp (c->Url, Url) == 0)
- {
- if (c->Flags & CACHE_VALID)
- {
- switch (CheckCacheTime (c, ExpireCacheTime)) {
- case 0:
- if (c == LastCache && difftime (ThisRequestTime, LastRequestTime) < ReloadCacheTime)
- {
- if (CheckCacheTime (c, 0) == -2) /* force queueing / reloading */
- {
- debug (("queued url on request\n"));
- ErrToReq (Req, 203, NULL, "Your request for reloading the document is queued.<BR>\n"
- "You will get the new document next time you are online.<BR>\n"
- "An expired cache entry exists and can be viewed by immedeately reloading this document.");
- return (2);
- }
- break; /* forcing reload of url */
- }
-
- if (! (Req->Stream = fopen (c->File, "r")) )
- {
- fprintf (LogStream, "cannot open cache file '%s', removing: %s\n", c->File, strerror (errno));
- debug (("cannot open cache file '%s', removing: %s\n", c->File, strerror (errno)));
- RemCacheEntry (c);
- }
- else
- {
- debug (("Found cache entry, file '%s'\n", c->File));
- Req->Cache = LastCache = c; /* ok, found cache entry */
- Req->Flags |= REQ_DONE;
- return (1);
- }
- break;
-
- /* case -1: break; */
- case -2:
- if (c == LastCache && difftime (ThisRequestTime, LastRequestTime) < ReloadCacheTime)
- {
- debug (("Sending (invalid) cache file '%s' on request\n", c->File));
- if (! (Req->Stream = fopen (c->File, "r")) )
- {
- fprintf (LogStream, "cannot open cache file '%s', removing: %s\n", c->File, strerror (errno));
- debug (("cannot open cache file '%s', removing: %s\n", c->File, strerror (errno)));
- RemCacheEntry (c);
- break;
- }
- else
- {
- Req->Cache = c; /* ok, send invalid cache entry */
- Req->Flags |= REQ_DONE;
- return (1);
- }
- }
- else
- {
- if (OffLine)
- {
- debug (("Cache entry expired, queued\n"));
- ErrToReq (Req, 203, NULL, "Your request is queued.<BR>\n"
- "You will get the document next time you are online.<BR>\n"
- "An expired cache entry exists and can be viewed by immedeately reloading this document.");
- LastCache = c;
- return (2);
- }
- else
- {
- debug (("unqueueing and getting Cache entry\n"));
- c->Flags = 0;
- c->File[0] = '_';
- LastCache = c;
- goto HaveAlreadyCache; /* sorry... */
- }
- }
- }
- }
- else /* c->Flags & CACHE_VALID */
- {
- if (c->Flags & CACHE_QUEUED)
- {
- if (OffLine)
- {
- if (c == LastCache && difftime (ThisRequestTime, LastRequestTime) < ReloadCacheTime)
- {
- errno = 0;
- ErrToReq (Req, 404, "Already queued", "Your request is already queued.<BR>\n"
- "You will get the document next time you are online.<BR>\n"
- "You tried to get an expired cache entry, but this document is not cached right now.");
- return (2);
- }
- else
- {
- debug (("Cache entry already queued\n"));
- errno = 0;
- ErrToReq (Req, 203, NULL, "Your request is already queued.<BR>\n"
- "You will get the document next time you are online.<BR>\n"
- "There is no expired cache entry for this document.");
- LastCache = c;
- return (2);
- }
- }
- else
- {
- debug (("unqueueing and getting Cache entry\n"));
- c->Flags = 0;
- c->File[0] = '_';
- LastCache = c;
- goto HaveAlreadyCache; /* sorry... */
- }
- }
- else
- {
- debug (("Second request while getting URL\n"));
- ErrToReq (Req, 204, NULL, "The same request was sent from another connection.\n"
- "May be you requested a queued URL which is just being received.<BR>\n"
- "Please try again in a few moments.");
- LastCache = c;
- return (2);
- }
- }
- }
- }
-
- if (! (Known || ProxyProxy || OffLine)) /* not cached, unknown and online without proxyproxy... don't know where to get it */
- {
- errno = 0;
- ErrToReq (Req, 404, "Unknown host", "You tried to get a document from an unknown host. Please check the URL.");
- debug (("#%02d: unknown host: URL '%s'\n", Req-Requests, Url));
- return (2);
- }
-
-
- if (! (c = GetFreeCacheSlot ()) ) /* not in cache so far */
- {
- if (OffLine)
- {
- ErrToReq (Req, 500, "Cache table full", "I'm sorry, but there's no memory left for the cache table.<BR>"
- "It is not possible to queue up your request.");
- return (2);
- }
- else
- {
- fprintf (LogStream, "Cache table full\n");
- debug (("Cache table full\n"));
- return (0); /* no more free entries - just proxy it */
- }
- }
-
- if (OffLine)
- {
- ErrToReq (Req, 203, NULL, "Your new request is queued.<BR>\n"
- "You will get the document next time you are online.");
- debug (("Request queued.\n"));
- strcpy (c->Url, Url);
- c->Flags = CACHE_QUEUED;
- LastCache = c;
- SaveCacheUrl (c);
- c->File[0] = '@';
- return (2);
- }
-
- HaveAlreadyCache:
-
- if (! (Req->Stream = fopen (c->File, "w")) )
- {
- fprintf (LogStream, "cannot open new cache file '%s': %s\n", c->File, strerror (errno));
- debug (("cannot open new cache file '%s': %s\n", c->File, strerror (errno)));
- c->File[0] = '\0';
- RemCacheEntry (c);
- return (0); /* just proxy it */
- }
-
- debug (("New cache file '%s'\n", c->File));
- strcpy (c->Url, Url); /* new cache entry */
- c->Flags = 0;
- Req->Cache = LastCache = c;
- return (0);
- }
-
-
- /*)) */
- /*(( "GetCacheData ()" */
-
- /* Fill request data space with data from cache
- * when a read error occures, no data will be received and ServWrite()
- * will automagically terminate the socket. */
-
- void GetCacheData (request_t *Req)
- {
- int Bytes;
-
- assert (Req->DataRecv < MAX_DATABUFFER);
- assert (Req->Stream);
-
- debug (("Getting more cache data - Url done: %s\n", Req->Flags & REQ_REQDONE ? "yes" : "no"));
-
- if ( (Bytes = fread (& Req->DataBuffer [Req->DataRecv], sizeof (char),
- MAX_DATABUFFER - Req->DataRecv, Req->Stream)) > 0)
- Req->DataRecv += Bytes;
- else
- if (! feof (Req->Stream))
- {
- fprintf (LogStream, "read error on cache file '%s': %s\n", Req->Cache->File, strerror (errno));
- debug (("read error on cache file '%s': %s\n", Req->Cache->File, strerror (errno)));
- }
- debug (("Read %d bytes\n", Bytes));
- }
-
-
- /*)) */
- /*(( "BuildFdSets ()" */
-
- /* Build fdsets (outstanding reads and writes) */
-
- void BuildFdSets (fd_set *ReadSet, fd_set *WriteSet)
- {
- int i;
- request_t *Req;
-
- FD_ZERO (ReadSet);
- FD_ZERO (WriteSet);
-
- debug (("Build: "));
- assert (RequestsFree >= 0);
- if (RequestsFree)
- {
- debug (("Server %d", (int) ServerSocket));
- FD_SET (ServerSocket, ReadSet);
- }
-
- for (i=0, Req=Requests; i < MaxRequests; i++, Req++)
- {
- if (Req->Flags & REQ_REQSOCKET)
- {
- assert (Req->ReqSocket != 0);
- if (Req->ReqRecv < MAX_REQBUFFER-1)
- {
- debug ((", Read Req %d", Req-Requests));
- FD_SET (Req->ReqSocket, ReadSet);
- }
- if (Req->DataRecv > Req->DataSent)
- {
- debug ((", Write Req %d", Req-Requests));
- FD_SET (Req->ReqSocket, WriteSet);
- }
- }
- if (Req->Flags & REQ_CONNSOCKET)
- {
- assert (Req->ConnSocket != 0);
- if (Req->ReqRecv > Req->ReqSent || Req->UrlRecv > Req->UrlSent) /* Waiting for connect or sending request */
- {
- debug ((", Write Con %d", Req-Requests));
- FD_SET (Req->ConnSocket, WriteSet);
- }
- if (Req->DataRecv < MAX_DATABUFFER) /* Transfer Data */
- {
- debug ((", Read Con %d", Req-Requests));
- FD_SET (Req->ConnSocket, ReadSet);
- }
- }
- }
- debug (("\n"));
- }
-
-
- /*)) */
- /*(( "CreateUrlRequest()/CheckUrl()" */
-
- /* Create Url request according to specification */
- /* Returns the length of the request. */
- /* Prot and Host may be NULL, in that case Name is to be asumed to be a full URL.
- * No checks for Proxyproxy mode are done in this case. */
-
- size_t CreateUrlRequest (const char *Method, const char *Prot, const char *Host, int Port, const char *Name, char *ReqSpace, int HttpOne)
- {
- char Buffer [MAX_URLBUFFER];
- char *p, *DokPrint;
-
- if (Prot && Host)
- {
- if (ProxyProxy)
- {
- if (Port > -1)
- sprintf (ReqSpace, "%s %s://%s:%d/", Method, Prot, Host, Port);
- else
- sprintf (ReqSpace, "%s %s://%s/", Method, Prot, Host);
- }
- else
- sprintf (ReqSpace, "%s /", Method);
- }
- else
- sprintf (ReqSpace, "%s ", Method);
-
- DokPrint = ReqSpace + strlen (ReqSpace);
-
- for (p = Buffer; *Name; )
- if (isvalidhttp (*Name))
- *p++ = *Name++;
- else
- {
- sprintf (p, "%%%02x", *Name++);
- p += 3;
- }
- *p = '\0';
-
- if (HttpOne)
- sprintf (DokPrint, "%s HTTP/1.0\r\n", Buffer);
- else
- sprintf (DokPrint, "%s\r\n", Buffer);
- debug (("Request '%s', len %d created.\n", ReqSpace, strlen (ReqSpace)));
- return (strlen (ReqSpace));
- }
-
-
- /* Check whether the URL is read completely. Sets REQ_REQDONE accordingly.
- * Attention! This routine relys on the fact, that ScanUrl was called already!
- * That means especially, that the first line was already read completely. */
- /* The routine does not check for Url termination on Buffer boundaries. So
- * the ReqBuffer has to be shifted always (four bytes remaining always) and
- * never be cleared at all. */
-
- void CheckUrl (request_t *Req)
- {
- debug (("Checking Url\n"));
- if (Req->Flags & REQ_HTTP1X0)
- {
- if (strstr (Req->ReqBuffer, "\n\n") || strstr (Req->ReqBuffer, "\r\r") ||
- strstr (Req->ReqBuffer, "\n\r\n\r") || strstr (Req->ReqBuffer, "\r\n\r\n"))
- Req->Flags |= REQ_REQDONE;
- }
- else
- Req->Flags |= REQ_REQDONE;
- #ifdef DEBUG
- if (Req->Flags & REQ_REQDONE)
- debug (("http request complete\n"));
- #endif
- }
-
-
- /*)) */
- /*(( "ScanUrl ()" */
-
- /* Scan the URL and divide it into several parts. Understands http/0.9
- * and http/1.0 versions right now. Sets REQ_REQDONE when URL is complete. */
- /* Req->UrlBuffer is valid after calling this routine and REQ_URLDONE set. */
- /* Returns -1 in case of failure, 0 on correct termination or found cache,
- * 1: found cache slot, FileD is open, 2: failure, cache contains error */
- /* Port and Address are value-return arguments. */
-
- int ScanUrl (request_t *Req, struct in_addr *Address, int *Port)
- {
- struct hostent *HostEnt;
- struct in_addr In;
- char BufMETH [12];
- char BufPROT [16];
- char BufDOK [MAX_URLSAVE + 256]; /* a critical array... but that's enough */
- char BufURL [128];
- char BufVERS [8];
- int CharsRead, CharsRead2, FoundHost = 0, i;
- char *DokPtr, *PortPtr;
- char *Host, *ObjectName;
-
- if (sscanf (Req->ReqBuffer, "%11s %15[a-zA-Z0-9]%n", BufMETH, BufPROT, &CharsRead) < 2)
- return (-1);
- debug (("METHOD '%s', PROT '%s'\n", BufMETH, BufPROT));
- if (CharsRead >= Req->ReqRecv)
- return (-1);
- if (OffLine && strcasecmp (BufMETH, "get") != 0) /* no get command */
- return (-1);
- if (! ProxyProxy)
- if (strcasecmp (BufPROT, "http") != 0) /* we only support http urls so far */
- return (-1);
- if (strncmp (& Req->ReqBuffer [CharsRead], "://", 3) != 0)
- return (-1);
- if (sscanf (& Req->ReqBuffer [CharsRead+3], "%126s%n", BufURL, &CharsRead2) < 1)
- return (-1);
- debug (("URL '%s'\n", BufURL));
- CharsRead += 3 + CharsRead2;
-
- if (! (DokPtr = strchr (BufURL, '/')) ) /* no host... */
- return (-1);
- *DokPtr++ = 0;
- ObjectName = DokPtr;
-
- if (strcasecmp (BufPROT, "http") != 0)
- *Port = -1;
- else
- *Port = StdHttpPort;
- if ( (PortPtr = strchr (BufURL, ':')) )
- {
- *PortPtr = 0;
- *Port = atoi (PortPtr + 1);
- }
-
- Host = BufURL;
- if (! (ProxyProxy || OffLine))
- {
- if ( (In.s_addr = inet_addr (BufURL)) != -1)
- FoundHost = 1;
- else if ( (HostEnt = gethostbyname (BufURL)) )
- {
- debug (("gethostbyname () succeded\n"));
- memmove (&In, HostEnt->h_addr, sizeof (struct in_addr));
- FoundHost = 1;
- }
- }
- else
- FoundHost = 1; /* we do know our proxy host */
-
- *Address = In;
-
- BufVERS [2] = 0; /* BufVERS is missused here... */
- if (*Port > -1)
- sprintf (BufDOK, "%s://%s:%d/", BufPROT, Host, *Port);
- else
- sprintf (BufDOK, "%s://%s/", BufPROT, Host);
- for (PortPtr = BufDOK + strlen (BufDOK); *DokPtr; DokPtr++, PortPtr++)
- {
- if (*DokPtr == '%')
- {
- if (! (BufVERS [0] = *++DokPtr) )
- break;
- if (! (BufVERS [1] = *++DokPtr) )
- break;
- if ( (i = strtol (BufVERS, NULL, 0x10)) )
- *PortPtr = i;
- else
- *PortPtr = '?';
- }
- else
- *PortPtr = *DokPtr;
- }
- *PortPtr = 0;
-
- debug (("Proto '%s', Host '%s', Port %d, Dok '%s'\n", BufPROT, Host, *Port, BufDOK));
-
- if (Req->ReqBuffer [CharsRead] == ' ' && CharsRead < Req->ReqRecv)
- if (sscanf (& Req->ReqBuffer [CharsRead], " %5s", BufVERS) == 1)
- if (strcasecmp (BufVERS, "http/") == 0)
- {
- debug (("got HTTP/1.0 or greater request\n"));
- Req->Flags |= REQ_HTTP1X0;
- }
-
- CheckUrl (Req);
- if (strlen (BufDOK) > MAX_URLSAVE-1) /* That one cannot be cached */
- {
- fprintf (LogStream, "ReqBuffer size (%d chars) exceeded - document '%s' is not cached\n", MAX_URLSAVE-1, BufDOK);
- debug (("ReqBuffer size (%d chars) exceeded - document '%s' is not cached\n", MAX_URLSAVE-1, BufDOK));
- return (0);
- }
-
- /* create UrlBuffer entry */
- assert (Req->UrlSent == 0);
- Req->UrlRecv = CreateUrlRequest (BufMETH, BufPROT, BufURL, *Port, ObjectName, Req->UrlBuffer, Req->Flags & REQ_HTTP1X0);
- PortPtr = strchr (& Req->ReqBuffer [CharsRead], '\n'); /* PortPtr und DokPtr are missused here */
- DokPtr = strchr (& Req->ReqBuffer [CharsRead], '\r');
- assert (DokPtr == NULL || PortPtr == NULL || PortPtr == DokPtr + 1 || DokPtr == PortPtr + 1);
- if (DokPtr == PortPtr + 1 || PortPtr == NULL)
- PortPtr = DokPtr;
- assert (PortPtr != NULL);
-
- Req->ReqSent = PortPtr - Req->ReqBuffer + 1;
- Req->Flags |= REQ_URLDONE;
-
- if (strcasecmp (BufMETH, "get") != 0) /* only get's will be cached */
- {
- debug (("no get method - no caching\n"));
- return (0);
- }
-
- return (ScanCache (Req, BufDOK, FoundHost));
- }
-
-
- /*)) */
- /*(( "DeleteRequest/Connect ()" */
-
- /* Close the connection socket only. When a request socket is still open
- * and no data is in the cache, close all.
- * Delete cache entry in case of an error. Check the answer whether it is
- * an error message or a regular answer. Errors should not be cached! */
- /* When Flags == 0 nothing is done at all */
-
- void DeleteConnect (request_t *Req, int ok)
- {
- /* cache_t tmp;*/
-
- debug (("DeleteCon %d\n", Req-Requests));
- if (! Req->Flags)
- return;
- if (Req->Flags & REQ_CONNSOCKET)
- {
- CloseSocket (Req->ConnSocket);
- Req->Flags &= ~REQ_CONNSOCKET;
- if (Req->Stream)
- fclose (Req->Stream);
- Req->Stream = NULL;
- if (ok)
- {
- if (Req->Cache && ! (Req->Cache->Flags & CACHE_QUEUED)) /* we've written into a cache file */
- {
- SaveCacheUrl (Req->Cache);
- Req->Cache = NULL;
- }
- }
- else if (Req->Cache) /* not ok - a error occured */
- {
- fprintf (LogStream, "error while receiving URL '%s' - removing\n", Req->Cache->Url);
- debug (("error while receiving URL '%s' - removing\n", Req->Cache->Url));
- Req->Cache->Url[0] = '\0';
- RemCacheEntry (Req->Cache);
- Req->Cache = NULL;
- }
- }
- if (Req->Flags & REQ_REQSOCKET)
- {
- if (Req->DataRecv <= Req->DataSent && (Req->Flags & REQ_REQDONE))
- {
- CloseSocket (Req->ReqSocket); /* All done. Hugh! */
- Req->Flags = 0;
- }
- else
- Req->Flags |= REQ_DONE;
- }
- else
- Req->Flags = 0;
- if (! Req->Flags)
- RequestsFree++;
- }
-
-
- /* Close the request socket only. When a connection socket is already up
- * (and enough data is already read) continue filling up the cache. When
- * everything is done, close all. The URL should be checked, too.
- * When it is not complete, all connections should be terminated
- * on CacheUnreadRequests false. */
- /* Don't call DeleteConnect when Flags == 0... */
-
- void DeleteRequest (request_t *Req, int ok)
- {
- debug (("DeleteReq %d\n", Req-Requests));
- if (Req->Flags & REQ_REQSOCKET)
- {
- CloseSocket (Req->ReqSocket);
- Req->Flags &= ~REQ_REQSOCKET;
- }
-
- if (Req->Flags & REQ_DONE) /* Maybe we're getting data from the cache */
- {
- if (Req->Stream)
- fclose (Req->Stream);
- Req->Stream = NULL;
- DeleteConnect (Req, ok);
- Req->Flags = 0; /* All done. Hugh! */
- debug (("All done - deleterequest\n"));
- }
- else if (Req->Flags & REQ_REQDONE)
- {
- if (! (Req->Flags & REQ_CONNSOCKET)) /* Aborting cache sending */
- DeleteConnect (Req, FALSE);
- else if (CacheUnreadRequests)
- Req->DataSent = Req->DataRecv = 0; /* Continue getting data into the cache */
- }
- else
- {
- Req->Flags |= REQ_DONE;
- DeleteConnect (Req, FALSE); /* No request known so far... */
- }
- }
-
-
- /*)) */
- /*(( "ServConnect ()" */
-
- /* The first line of the URL is already there, so we can connect to the
- * remote host. */
-
- void ServConnect (request_t *Req)
- {
- struct sockaddr_in SockIn;
- struct sockaddr_in *SockPtr = &SockIn;
- ioctl_t on = 1;
- int Port;
-
- debug (("ServCon for Req %d; Flags %x\n", Req-Requests, Req->Flags));
-
- if (Req->Flags & REQ_REQSOCKET)
- {
- if (strchr (Req->ReqBuffer, '\n') == 0 &&
- strchr (Req->ReqBuffer, '\r') == 0) /* first line of URL is not there... */
- return;
-
- if (Req->Flags & REQ_URLDONE) /* already set up */
- {
- CheckUrl (Req);
- return;
- }
- }
-
- memset ((char *) &SockIn, 0, sizeof (struct sockaddr_in));
-
- switch (ScanUrl (Req, &SockIn.sin_addr, &Port)) {
- case 0: /* need to get document from remote host */
- if (Req->Cache)
- {
- fprintf (LogStream, "#%02d: getting URL '%s'\n", Req-Requests, Req->Cache->Url);
- debug (("#%02d: getting URL '%s'\n", Req-Requests, Req->Cache->Url));
- }
- else
- {
- fprintf (LogStream, "#%02d: serving uncacheable request '%s'\n",
- Req-Requests, Req->ReqBuffer);
- debug (("#%02d: serving uncacheable request '%s'\n",
- Req-Requests, Req->ReqBuffer));
- }
- break;
-
- case 1: /* already cached - no need to connect to remote host */
- assert (Req->Cache != NULL);
- fprintf (LogStream, "#%02d: sending URL '%s' from cache '%s'\n",
- Req-Requests, Req->Cache->Url, Req->Cache->File);
- debug (("#%02d: sending URL '%s' from cache '%s'\n",
- Req-Requests, Req->Cache->Url, Req->Cache->File));
- GetCacheData (Req);
- return;
-
- case 2: /* send error message from buffer */
- return;
-
- default:
- {
- fprintf (LogStream, "#%02d: invalid request\n", Req-Requests);
- debug (("#%02d: invalid request\n", Req-Requests));
- Req->Flags |= REQ_DONE | REQ_REQDONE;
- errno = 0;
- ErrToReq (Req, 500, "Invalid Request", "Your Request is not a valid URL.<BR>\n"
- "Please check it and try again.");
- return;
- }
- }
-
- if (OffLine)
- {
- errno = 0;
- ErrToReq (Req, 500, "Unable to serv", "It is not possible to connect to a remote host in offline mode.<BR>\n"
- "Either the requested URL is to long to be queued or an internal error has occured. When the\n"
- "URL is rather short, please contact the author of" PRG_NAME);
- return;
- }
-
- if (ProxyProxy)
- SockPtr = &ProxyProxyIn;
- else
- {
- SockIn.sin_family = AF_INET;
- SockIn.sin_port = htons (Port);
- }
-
-
- if ( (Req->ConnSocket = socket (AF_INET, SOCK_STREAM, 0)) < 0) /* open connection */
- {
- syslog (LOG_ERR, "%s: socket () failed", PrgName);
- DeleteRequest (Req, FALSE);
- return;
- }
-
- Req->Flags |= REQ_CONNSOCKET;
-
- #ifdef FIOASYNC
- if (ioctl (Req->ConnSocket, FIOASYNC, (caddr_t) &on) < 0)
- {
- syslog (LOG_ERR, "%s: ioctl() failed for FIOASYNC: %s", PrgName, strerror (errno));
- DeleteRequest (Req, FALSE);
- return;
- }
- #endif
- #ifdef FIONBIO
- if (ioctl (Req->ConnSocket, FIONBIO, (caddr_t) &on) < 0)
- {
- syslog (LOG_ERR, "%s: ioctl() failed for FIONBIO: %s", PrgName, strerror (errno));
- DeleteRequest (Req, FALSE);
- return;
- }
- #endif
-
- if (connect (Req->ConnSocket, (struct sockaddr *) SockPtr, sizeof (struct sockaddr_in)) < 0)
- {
- if (errno != EINPROGRESS)
- {
- if (ProxyProxy)
- ErrToReq (Req, 500, "Proxyproxy Host Unreachable",
- "The host you specified with the 'proxy' option can't be contacted.<BR>\n"
- "Please wait until the proxy is up again or start " PRG_NAME " in offline mode.");
- else
- ErrToReq (Req, 500, "Host Unreachable",
- "The specified host can't be contacted.<BR>\n"
- "Please wait until it is up again or try any other URLs.");
- return;
- }
- }
- debug (("ServCon -> Conn %d\n", (int) Req->ConnSocket));
- }
-
-
- /*)) */
- /*(( "ServServer ()" */
-
- /* Serv the server port. Accept new connections, set up request database */
-
- void ServServer (void)
- {
- struct sockaddr_in PeerIn;
- len_t Len = sizeof (struct sockaddr_in);
- request_t *Req = Requests;
- ioctl_t on = 1;
-
- assert (RequestsFree > 0);
-
- while (Req->Flags) /* any Flags set -> occupied */
- Req++;
- debug (("accept Req %d\n", Req-Requests));
- assert (Req - Requests < MaxRequests);
-
- if ( (Req->ReqSocket = accept (ServerSocket, (struct sockaddr *) &PeerIn, &Len)) < 0)
- {
- syslog (LOG_ERR, "%s: accept() failed: %s", PrgName, strerror (errno));
- return;
- }
-
- fprintf (LogStream, "#%02d: new request from %s:%d\n",
- Req-Requests, inet_ntoa (PeerIn.sin_addr), PeerIn.sin_port);
- debug (("#%02d: new request from %s:%d\n",
- Req-Requests, inet_ntoa (PeerIn.sin_addr), PeerIn.sin_port));
-
- Req->Flags |= REQ_REQSOCKET;
- Req->ReqBuffer[0] = Req->UrlBuffer [0] = Req->DataBuffer [0] = '\0';
- Req->ReqSent = Req->ReqRecv = Req->UrlSent = Req->UrlRecv =
- Req->DataSent = Req->DataRecv = 0;
- Req->Cache = NULL;
- Req->Stream = NULL;
- RequestsFree--;
-
- #ifdef FIOASYNC
- if (ioctl (Req->ReqSocket, FIOASYNC, (caddr_t) &on) < 0)
- {
- syslog (LOG_ERR, "%s: ioctl() failed for FIOASYNC: %s", PrgName, strerror (errno));
- DeleteRequest (Req, FALSE);
- return;
- }
- #endif
- #ifdef FIONBIO
- if (ioctl (Req->ReqSocket, FIONBIO, (caddr_t) &on) < 0)
- {
- syslog (LOG_ERR, "%s: ioctl() failed for FIONBIO: %s", PrgName, strerror (errno));
- DeleteRequest (Req, FALSE);
- return;
- }
- #endif
- }
-
-
- /*)) */
- /*(( "ServRead ()" */
-
- /* Server for all reads */
-
- void ServRead (fd_set *ReadSet)
- {
- int i, Bytes;
- register request_t *Req;
-
- if (FD_ISSET (ServerSocket, ReadSet))
- ServServer ();
- for (i=0, Req=Requests; i < MaxRequests; i++, Req++)
- {
- if ((Req->Flags & REQ_REQSOCKET) && FD_ISSET (Req->ReqSocket, ReadSet))
- {
- assert (Req->ReqRecv < MAX_REQBUFFER-1);
- if ( (Bytes = recv (Req->ReqSocket, & Req->ReqBuffer [Req->ReqRecv],
- (long) MAX_REQBUFFER-1 - Req->ReqRecv, 0)) < 0)
- {
- fprintf (LogStream, "#%02d: on receiving URL: %s\n", Req-Requests, strerror (errno));
- debug (("#%02d: on receiving URL: %s\n", Req-Requests, strerror (errno)));
- DeleteRequest (Req, FALSE);
- }
- else
- {
- Req->ReqRecv += Bytes;
- Req->ReqBuffer [Req->ReqRecv] = '\0'; /* needed for strchr() */
-
- debug (("Read %d bytes from Req %d to 0x%x containing:\n'%s'\n", Bytes,
- Req-Requests, Req->ReqRecv-Bytes, & Req->ReqBuffer [Req->ReqRecv - Bytes]));
- #if defined (DEBUG) && ! defined (_M68020)
- if ((Req->ReqRecv - Bytes) & 0x0001)
- debug (("(odd address...\n"));
- else
- #endif
- debug (("= 0x %08lx %08lx %08lx %08lx\n", * (long *) (Req->ReqBuffer + Req->ReqRecv - Bytes),
- * (long *) (Req->ReqBuffer + Req->ReqRecv - Bytes + 4), * (long *) (Req->ReqBuffer + Req->ReqRecv - Bytes + 8),
- * (long *) (Req->ReqBuffer + Req->ReqRecv - Bytes + 12)));
-
- if (Bytes == 0) /* request socket gone away */
- {
- DeleteRequest (Req, TRUE);
- continue;
- }
- ServConnect (Req); /* Check if anything can be done already */
- if ((Req->Flags & REQ_DONE) && (Req->Flags & REQ_REQDONE) && (Req->DataRecv == 0))
- {
- DeleteRequest (Req, TRUE); /* all done, another time */
- continue;
- }
- if ((Req->ReqRecv == MAX_REQBUFFER-1) && ! (Req->Flags & REQ_CONNSOCKET))
- { /* buffer full and no connection yet */
- if (Req->Flags & REQ_URLDONE)
- Req->ReqRecv = Req->ReqSent = 0; /* Skip remaining data (we won't send it...) */
- else
- {
- fprintf (LogStream, "#%02d: URL buffer overflow\n", Req-Requests);
- debug (("#%02d: URL buffer overflow\n", Req-Requests));
- DeleteRequest (Req, FALSE);
- }
- }
- }
- }
- if ((Req->Flags & REQ_CONNSOCKET) && FD_ISSET (Req->ConnSocket, ReadSet))
- {
- assert (Req->DataRecv < MAX_DATABUFFER);
- if ( (Bytes = recv (Req->ConnSocket, & Req->DataBuffer [Req->DataRecv],
- (long) MAX_DATABUFFER - Req->DataRecv, 0)) < 0)
- {
- if (ProxyProxy)
- ErrToReq (Req, 500, "Proxyproxy Host Unreachable / read() failed",
- "The host you specified with the 'proxy' option can't be contacted.<BR>\n"
- "Please wait until the proxy is up again or start " PRG_NAME " in offline mode.");
- else
- ErrToReq (Req, 500, "Host Unreachable / read() failed",
- "The specified host can't be contacted.<BR>\n"
- "Please wait until it is up again or try any other URLs.");
- DeleteConnect (Req, FALSE);
- }
- else
- {
- debug (("Read %d bytes from Con %d\n", Bytes, Req-Requests));
- if (Bytes == 0) /* We're done */
- {
- DeleteConnect (Req, TRUE);
- continue;
- }
- else if (Req->Stream)
- fwrite (& Req->DataBuffer [Req->DataRecv], sizeof (char), /* no error checking... */
- Bytes, Req->Stream);
- if (Req->Flags & REQ_REQSOCKET)
- Req->DataRecv += Bytes; /* else: only save cache data */
- }
- }
- }
- }
-
-
- /*)) */
- /*(( "ServWrite ()" */
-
- /* Server for all writes */
-
- void ServWrite (fd_set *WriteSet)
- {
- int i, Bytes;
- register request_t *Req;
-
- for (i=0, Req=Requests; i < MaxRequests; i++, Req++)
- {
- if ((Req->Flags & REQ_REQSOCKET) && FD_ISSET (Req->ReqSocket, WriteSet))
- {
- assert (Req->DataRecv > Req->DataSent);
- if ( (Bytes = send (Req->ReqSocket, & Req->DataBuffer [Req->DataSent],
- (long) Req->DataRecv - Req->DataSent, 0)) < 0)
- {
- fprintf (LogStream, "#%02d: on sending data: %s\n", Req-Requests, strerror (errno));
- debug (("#%02d: on sending data: %s\n", Req-Requests, strerror (errno)));
- DeleteRequest (Req, FALSE);
- continue;
- }
- else
- {
- debug (("Wrote %d bytes to Req %d\n", Bytes, Req-Requests));
- Req->DataSent += Bytes;
- assert (Req->DataRecv >= Req->DataSent);
- if (Bytes == 0) /* request socket is unable to receive data */
- {
- fprintf (LogStream, "#%02d: request socket unable to receive data\n", Req-Requests);
- debug (("#%02d: request socket unable to receive data\n", Req-Requests));
- DeleteRequest (Req, FALSE);
- continue;
- }
-
- if (Req->DataSent == Req->DataRecv) /* clear / shift data buffer */
- Req->DataSent = Req->DataRecv = 0;
- else if (Req->DataSent > SHIFT_DATABUFFER)
- {
- debug (("shifting databuffer size %d by %d bytes\n", Req->DataRecv - Req->DataSent, Req->DataSent));
- memmove (Req->DataBuffer, & Req->DataBuffer [Req->DataSent],
- Req->DataRecv - Req->DataSent);
- Req->DataRecv -= Req->DataSent;
- Req->DataSent = 0;
- }
- if (Req->DataRecv < MAX_DATABUFFER && (Req->Flags & REQ_DONE) && Req->Stream)
- GetCacheData (Req);
- if (Req->DataRecv == 0 && (Req->Flags & REQ_DONE) && (Req->Flags & REQ_REQDONE)) /* We're already done */
- DeleteRequest (Req, TRUE);
-
- }
- }
- if ((Req->Flags & REQ_CONNSOCKET) && FD_ISSET (Req->ConnSocket, WriteSet))
- {
- if (Req->UrlRecv > Req->UrlSent)
- Bytes = send (Req->ConnSocket, &Req->UrlBuffer [Req->UrlSent],
- (long) Req->UrlRecv - Req->UrlSent, 0);
- else
- {
- assert (Req->ReqRecv > Req->ReqSent);
- Bytes = send (Req->ConnSocket, & Req->ReqBuffer [Req->ReqSent],
- (long) Req->ReqRecv - Req->ReqSent, 0);
- }
- if (Bytes < 0)
- {
- if (ProxyProxy)
- {
- fprintf (LogStream, "#%02d: proxyproxy host unreachable: %s\n", Req-Requests, strerror (errno));
- debug (("#%02d: proxyproxy host unreachable: %s\n", Req-Requests, strerror (errno)));
- }
- else
- {
- fprintf (LogStream, "#%02d: on sending url: %s\n", Req-Requests, strerror (errno));
- debug (("#%02d: on sending url: %s\n", Req-Requests, strerror (errno)));
- }
- DeleteConnect (Req, FALSE);
- }
- else
- {
- debug (("Wrote %d bytes to Conn %d from %s, 0x%x\n", Bytes, Req-Requests,
- Req->UrlRecv > Req->UrlSent ? "UrlBuffer" : "ReqBuffer", Req->UrlRecv > Req->UrlSent ? Req->UrlSent : Req->ReqSent));
-
- if (Req->UrlRecv > Req->UrlSent)
- Req->UrlSent += Bytes;
- else
- Req->ReqSent += Bytes;
- if (Bytes == 0) /* connection socket is unable to receive url */
- {
- fprintf (LogStream, "#%02d: connection socket unable to receive url\n", Req-Requests);
- debug (("#%02d: connection socket unable to receive url\n", Req-Requests));
- DeleteConnect (Req, FALSE);
- continue;
- }
-
- if (Req->ReqSent > SHIFT_REQBUFFER)
- {
- debug (("shifting reqbuffer size %d+4 by %d bytes\n", Req->ReqRecv - Req->ReqSent, Req->ReqSent-4));
- memmove (Req->ReqBuffer, & Req->ReqBuffer [Req->ReqSent - 4],
- Req->ReqRecv - Req->ReqSent + 4);
- Req->ReqRecv -= Req->ReqSent - 4;
- Req->ReqSent = 4;
- }
- }
- }
- }
- }
-
-
- /*)) */
- /*(( "CacheInUse()/RequestQueued()" */
-
- /* Check wheather a cache entry is used in any other request */
-
- request_t *CacheInUse (cache_t *c)
- {
- request_t *Req = Requests;
- int i;
-
- for (i = 0; i < MaxRequests; i++, Req++)
- if (Req->Cache == c)
- return (Req);
- return (NULL);
- }
-
-
- /* Initiate contact to remote host for a queued request */
-
- void RequestQueued (cache_t *c)
- {
- request_t *Req = Requests;
- int Len;
-
- assert (MAX_REQBUFFER >= 4 * MAX_URLSAVE); /* to be on the save side */
- fprintf (LogStream, "initiating request for Url '%s'\n", c->Url);
-
- while (Req->Flags) /* any Flags set -> occupied */
- Req++;
- assert (Req - Requests < MaxRequests);
-
- Req->Flags = REQ_REQDONE;
- Req->ReqSent= Req->DataSent = Req->DataRecv = Req->UrlSent = Req->UrlRecv = 0;
- Req->Cache = NULL;
- Req->Stream = NULL;
- RequestsFree--;
-
- Len = CreateUrlRequest ("GET", NULL, NULL, 0, c->Url, Req->ReqBuffer, TRUE);
- sprintf (& Req->ReqBuffer [Len], "User-Agent: %s\r\nAccept: */*\r\n\r\n", VERSHTTP);
- Req->ReqRecv = strlen (Req->ReqBuffer);
- debug (("initiating request '%s'\n", Req->ReqBuffer));
- if (c->File [0] == '@')
- c->Url [0] = '\0';
- RemCacheEntry (c);
- ServConnect (Req);
- }
-
-
- /*)) */
- /*(( "CheckGetQueued()" */
-
- /* Check number of pending requests and initiate getting of queued URLs */
-
- void CheckGetQueued (void)
- {
- static cachearray_t *a = NULL;
- static int Nr = 0;
- static int State = 1;
- cache_t *c;
-
- if (State == 3 || ! GetQueued)
- return;
- if (a == NULL)
- a = Caches;
-
- debug (("State %d\n", State));
-
- while (RequestsFree > MIN_REQUESTS)
- switch (State) {
- case 1: /* get all CACHE_QUEUED & ! CACHE_VALID */
- for (c = & a->Caches [Nr]; Nr < MAX_CACHES; Nr++, c++)
- if ((c->Flags & CACHE_QUEUED) && ! (c->Flags & CACHE_VALID)) /* get urls without expired caches first */
- if (! CacheInUse (c)) /* inUse caches won't be get at all... */
- {
- RequestQueued (c);
- break;
- }
- if (Nr < MAX_CACHES)
- continue;
- Nr = 0;
- if (a->Next)
- {
- a = a->Next;
- continue;
- }
-
- State = 2;
- a = Caches; /* no break! */
-
- case 2:
- for (c = & a->Caches [Nr]; Nr < MAX_CACHES; Nr++, c++)
- if (c->Flags & CACHE_QUEUED)
- {
- assert (c->Flags & CACHE_VALID);
- if (! CacheInUse (c))
- {
- RequestQueued (c);
- break;
- }
- }
- if (Nr < MAX_CACHES)
- continue;
- Nr = 0;
- if (a->Next)
- {
- a = a->Next;
- continue;
- }
- State = 3;
- return;
-
- default:
- assert (0);
- }
- }
-
-
- /*)) */
- /*(( "DeleteInvalidCaches()" */
-
- /* Shutdown: Close and delete all invalid cache entries. */
-
- void DeleteInvalidCaches (void)
- {
- cachearray_t *a;
- cache_t *c, Tmp, *cc;
- request_t *Req;
- int i;
-
- for (a = Caches; a; a = a->Next)
- for (i = 0, c = a->Caches; i < MAX_CACHES; i++, c++)
- if (c->File[0] && (c->Flags & (CACHE_VALID | CACHE_QUEUED)) +0 == 0) /* !!! */
- {
- Req = CacheInUse (c);
- if (Req)
- {
- if (Req->Stream) /* close cache stream */
- fclose (Req->Stream);
- Req->Stream = NULL;
- if (c->Url[0])
- {
- fprintf (LogStream, "removing and queueing URL '%s'\n", c->Url);
- SaveCacheUrl (c);
- assert (c->Url[0] != '\0');
- Tmp = *c;
- c->Url[0] = '\0'; /* keep the Urlfile */
- RemCacheEntry (c);
- cc = GetFreeCacheSlot ();
- assert (cc != NULL);
- InitCacheSlot (cc, &Tmp, '@', '.');
- cc->Flags = CACHE_QUEUED;
- }
- else
- RemCacheEntry (c);
- }
- else
- debug (("File '%s', Url '%s', Flags 0x%x without Request!\n", c->File, c->Url, c->Flags));
- }
- }
-
-
- /*)) */
- /*(( "main ()" */
-
- /* The main routine */
-
- void main (int argc, char **argv)
- {
- fd_set ReadSet;
- fd_set WriteSet;
- char *ProxyHost = NULL;
- char *LogFile = "." PRG_NAME "-log";
- int ProxyPort;
-
- PrgName = *argv++;
-
- if (--argc == 1)
- if (**argv == '?')
- {
- fprintf (stderr, "Usage: %s [proxy PROXYHOST PROXYPORT] [port PORT] [cache DIR] [del SECONDS]\n"
- "[expire SECONDS] [reload SECONDS] [log FILE] [numreq NUMBER]\n"
- "[unread] [offline] [get]\n"
- "The cache keyword will change the local directory.\n", PrgName);
- exit (0);
- }
-
- while (argc)
- {
- if (strcasecmp (*argv, "proxy") == 0)
- {
- if (argc < 3)
- {
- fprintf (stderr, "%s: need two arguments for 'proxy'\n", PrgName);
- exit (1);
- }
- argv++;
- argc -= 3;
- ProxyHost = *argv++;
- if ( (ProxyPort = atoi (*argv++)) <= 0)
- {
- fprintf (stderr, "%s: Wrong second argument for 'proxy' (need the port number).\n", PrgName);
- exit (1);
- }
- }
- else if (strcasecmp (*argv, "port") == 0)
- {
- if (argc < 2)
- {
- fprintf (stderr, "%s: need a argument for 'port'\n", PrgName);
- exit (1);
- }
- argv++;
- argc -= 2;
- ServerPort = atoi (*argv++);
- }
- else if (strcasecmp (*argv, "cache") == 0)
- {
- if (argc < 2)
- {
- fprintf (stderr, "%s: need a argument for 'cache'\n", PrgName);
- exit (1);
- }
- argv++;
- argc -= 2;
- if (chdir (*argv++))
- {
- fprintf (stderr, "%s: no directory '%s': %s\n", PrgName, argv[-1], strerror (errno));
- exit (1);
- }
- }
- else if (strcasecmp (*argv, "del") == 0)
- {
- if (argc < 2)
- {
- fprintf (stderr, "%s: need a argument for 'del'\n", PrgName);
- exit (1);
- }
- argv++;
- argc -= 2;
- DelCacheTime = atoi (*argv++);
- }
- else if (strcasecmp (*argv, "expire") == 0)
- {
- if (argc < 2)
- {
- fprintf (stderr, "%s: need a argument for 'expire'\n", PrgName);
- exit (1);
- }
- argv++;
- argc -= 2;
- ExpireCacheTime = atoi (*argv++);
- }
- else if (strcasecmp (*argv, "reload") == 0)
- {
- if (argc < 2)
- {
- fprintf (stderr, "%s: need a argument for 'reload'\n", PrgName);
- exit (1);
- }
- argv++;
- argc -= 2;
- ReloadCacheTime = atoi (*argv++);
- }
- else if (strcasecmp (*argv, "log") == 0)
- {
- if (argc < 2)
- {
- fprintf (stderr, "%s: need a argument for 'log'\n", PrgName);
- exit (1);
- }
- argv++;
- argc -= 2;
- LogFile = *argv++;
- }
- else if (strcasecmp (*argv, "numreq") == 0)
- {
- if (argc < 2)
- {
- fprintf (stderr, "%s: need a argument for 'numreq'\n", PrgName);
- exit (1);
- }
- argv++;
- argc -= 2;
- if ( (MaxRequests = atoi (*argv++)) <= MIN_REQUESTS)
- {
- fprintf (stderr, "%s: argument 'numreq' needs nummeric value > %d\n", PrgName, MIN_REQUESTS);
- exit (1);
- }
- if (MaxRequests * 3 >= FD_SETSIZE - 4)
- {
- fprintf (stderr, "%s: argument 'numreq' maximum value of %d exceeded\n", PrgName, (FD_SETSIZE - 5) / 3);
- exit (1);
- }
- }
- else if (strcasecmp (*argv, "unread") == 0)
- {
- argv++;
- argc--;
- CacheUnreadRequests = TRUE;
- }
- else if (strcasecmp (*argv, "offline") == 0)
- {
- argv++;
- argc--;
- if (GetQueued)
- {
- fprintf (stderr, "%s: you cannot specify both, 'get' and 'offline'", PrgName);
- exit (1);
- }
- OffLine = 1;
- }
- else if (strcasecmp (*argv, "get") == 0)
- {
- argv++;
- argc--;
- if (OffLine)
- {
- fprintf (stderr, "%s: you cannot specify both, 'get' and 'offline'", PrgName);
- exit (1);
- }
- GetQueued = 1;
- }
- else
- {
- fprintf (stderr, "%s: unknown option '%s'\n", PrgName, *argv);
- exit (1);
- }
- }
-
- assert (FD_SETSIZE > MaxRequests * 3);
-
- Init (ProxyHost, ProxyPort, LogFile);
-
- if (ReadCacheList ()) /* Read cache entries or rebuild them */
- BuildCache ();
-
- /* Never return */
- for (;;)
- {
- CheckGetQueued ();
- BuildFdSets (&ReadSet, &WriteSet);
- debug (("Select... RequestsFree %d, CachesFree %d\n", RequestsFree, CachesFree));
- if (select (FD_SETSIZE, &ReadSet, &WriteSet, NULL, NULL) <= 0)
- {
- if (errno == EINTR)
- {
- fprintf (stderr, "%s: terminating due to signal - saving cachetable\n", PrgName);
- fprintf (LogStream, "terminating due to signal - saving cachetable\n");
- DeleteInvalidCaches ();
- SaveCacheList ();
- fprintf (stderr, "done\n", PrgName);
- fprintf (LogStream, "done\n\n");
- fclose (LogStream);
- exit (0);
- }
- syslog (LOG_ERR, "%s: select failed: %s", PrgName, strerror (errno));
- }
- else
- {
- ServRead (&ReadSet);
- ServWrite (&WriteSet);
- }
- }
- }
- /*)) */
-
-